#include "decode.h"

#include <QFile>

Decode::Decode()
{
    outfilename = "tmp%d";


    avcodec_register_all();

    av_init_packet(&avpkt);
    /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
    //    memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);
    printf("Decode video file to %s\n", outfilename);
    /* find the mpeg1 video decoder */
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    c = NULL;
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    if(codec->capabilities&CODEC_CAP_TRUNCATED)
        c->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */
    c->width = 320;
    c->height = 240;

    c->time_base.num = 1;
    c->time_base.den = 30;
    c->bit_rate = 200000;
    c->gop_size = 10;
    c->max_b_frames = 2;

    av_opt_set(c->priv_data, "tune", "fastdecode", 0);
    av_opt_set(c->priv_data, "preset", "superfast", 0);
    av_opt_set(c->priv_data, "tune", "zerolatency", 0);
    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }

    frame_count = 0;
    sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
                             c->width, c->height, AV_PIX_FMT_RGB24,
                             SWS_AREA, NULL, NULL, NULL);
    if (!sws_ctx) {
        fprintf(stderr,
                "sws_getContext failed!\n");
    }

    int ret = 0;
    if ((ret = av_image_alloc(tmp_data, tmp_linesize,
                              c->width, c->height,
                              AV_PIX_FMT_RGB24, 1)) < 0) {
        fprintf(stderr, "Could not allocate destination image\n");
    }
    reslut_size = ret;
}

Decode::~Decode()
{
    int got_frame = 0;
    //    /* some codecs, such as MPEG, transmit the I and P frame with a
    //       latency of one frame. You must do the following to have a
    //       chance to get the last frame of the video */
    avpkt.data = NULL;
    avpkt.size = 0;
    //    decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 1);
    int len;
//    char buf[1024];

    len = avcodec_decode_video2(c, frame, &got_frame, &avpkt);
    if (len < 0) {
        fprintf(stderr, "Error while decoding frame %d\n", frame_count);
        return;
    }
    if (got_frame) {
//        printf("Saving frame %3d\n", frame_count);
        fflush(stdout);
        /* the picture is allocated by the decoder, no need to free it */
//        snprintf(buf, sizeof(buf), outfilename, frame_count);
//        pgm_save(frame->data[0], frame->linesize[0],
//                c->width, c->height, buf);
        frame_count++;
    }
    if (avpkt.data) {
        avpkt.size -= len;
        avpkt.data += len;
    }


//    fclose(f);
    avcodec_close(c);
    av_free(c);
    av_frame_free(&frame);
    sws_freeContext(sws_ctx);
    printf("\n");
}

int Decode::decodeData(Data::DataPtr data)
{
    if (data->getValidDataSize() == 0) {
        return -1;
    }

    int got_frame = 0;

    avpkt.size = data->getValidDataSize();
    if (avpkt.size == 0)
        return -1;
    /* NOTE1: some codecs are stream based (mpegvideo, mpegaudio)
       and this is the only method to use them because you cannot
       know the compressed data size before analysing it.
       BUT some other codecs (msmpeg4, mpeg4) are inherently frame
       based, so you must call them with all the data for one
       frame exactly. You must also initialize 'width' and
       'height' before initializing them. */
    /* NOTE2: some codecs allow the raw parameters (frame size,
       sample rate) to be changed at any frame. We handle this, so
       you should also take care of it */
    /* here, we use a stream based decoder (mpeg1video), so we
       feed decoder and see if it could decode a frame */
    avpkt.data = (u_int8_t *)data->GetPictureDataPtr();
    while (avpkt.size > 0)
        //        if (decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 0) < 0)
        //        exit(0);
    {
        int len = 0;
//        char buf[1024];
        len = avcodec_decode_video2(c, frame, &got_frame, &avpkt);
        if (len < 0) {
            fprintf(stderr, "Error while decoding frame %d\n", frame_count);
            data->setValidDataSize(0);
            return len;
        }
        if (got_frame) {
//            printf("Saving frame %3d\n", frame_count);
            fflush(stdout);
            /* the picture is allocated by the decoder, no need to free it */
            //            snprintf(buf, sizeof(buf), outfilename, frame_count);
            //            pgm_save(frame->data[0], frame->linesize[0],
            //                     c->width, c->height, buf);


            QFile tmpFile("test.yuv");
            if (!tmpFile.open(QIODevice::WriteOnly)) {
                std::cout << "open file failed!" << std::endl;
                return -1;
            }
            int writebyte = frame->linesize[0], num;
            while (writebyte > 0) {
             num = tmpFile.write((char*)(frame->data[0]), writebyte);
             writebyte -= num;
            }
            writebyte = frame->linesize[1];
            while (writebyte > 0) {
             num = tmpFile.write((char*)(frame->data[1]), writebyte);
             writebyte -= num;
            }
            writebyte = frame->linesize[2];
            while (writebyte > 0) {
             num = tmpFile.write((char*)(frame->data[2]), writebyte);
             writebyte -= num;
            }

             tmpFile.close();


            sws_scale(sws_ctx, (const uint8_t * const*)frame->data,
                      frame->linesize, 0, c->height, tmp_data,
                      tmp_linesize);
            for (int i = 0; i < c->height; i++) {
                for (int j = 0; j < c->width; j++) {
                    u_int8_t tmpValue = tmp_data[0][(i * c->width + j) * 3 + 0];
                    tmp_data[0][(i * c->width + j) * 3 + 0] = tmp_data[0][(i * c->width + j) * 3 + 1];
                    tmp_data[0][(i * c->width + j) * 3 + 1] = tmp_data[0][(i * c->width + j) * 3 + 2];
                    tmp_data[0][(i * c->width + j) * 3 + 2] = tmpValue;
//                    memset(tmp_data[0], 255, reslut_size);
//                    std::cout << c->height << "\t" << c->width << std::endl;
                 }
            }
            memcpy(data->GetPictureDataPtr(), tmp_data[0], reslut_size);
//            std::cout << reslut_size << std::endl;
            data->setValidDataSize(reslut_size);
            frame_count++;
        }
        if (avpkt.data) {
            avpkt.size -= len;
            avpkt.data += len;
        }
    }

    return 0;
}

void Decode::pgm_save(unsigned char *buf, int wrap,
                      int xsize, int ysize, char *filename)
{
    FILE *f;
    int i;
    f = fopen(filename,"w");
    fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
    for (i = 0; i < ysize; i++)
        fwrite(buf + i * wrap, 1, xsize, f);
    fclose(f);
}


const int Table_fv1[256]={ -180, -179, -177, -176, -174, -173, -172, -170, -169, -167, -166, -165, -163, -162, -160, -159, -158, -156, -155, -153, -152, -151, -149, -148, -146, -145, -144, -142, -141, -139, -138, -137, -135, -134, -132, -131, -130, -128, -127, -125, -124, -123, -121, -120, -118, -117, -115, -114, -113, -111, -110, -108, -107, -106, -104, -103, -101, -100, -99, -97, -96, -94, -93, -92, -90, -89, -87, -86, -85, -83, -82, -80, -79, -78, -76, -75, -73, -72, -71, -69, -68, -66, -65, -64, -62, -61, -59, -58, -57, -55, -54, -52, -51, -50, -48, -47, -45, -44, -43, -41, -40, -38, -37, -36, -34, -33, -31, -30, -29, -27, -26, -24, -23, -22, -20, -19, -17, -16, -15, -13, -12, -10, -9, -8, -6, -5, -3, -2, 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 16, 18, 19, 21, 22, 23, 25, 26, 28, 29, 30, 32, 33, 35, 36, 37, 39, 40, 42, 43, 44, 46, 47, 49, 50, 51, 53, 54, 56, 57, 58, 60, 61, 63, 64, 65, 67, 68, 70, 71, 72, 74, 75, 77, 78, 79, 81, 82, 84, 85, 86, 88, 89, 91, 92, 93, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, 112, 113, 114, 116, 117, 119, 120, 122, 123, 124, 126, 127, 129, 130, 131, 133, 134, 136, 137, 138, 140, 141, 143, 144, 145, 147, 148, 150, 151, 152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 166, 168, 169, 171, 172, 173, 175, 176, 178 };
const int Table_fv2[256]={ -92, -91, -91, -90, -89, -88, -88, -87, -86, -86, -85, -84, -83, -83, -82, -81, -81, -80, -79, -78, -78, -77, -76, -76, -75, -74, -73, -73, -72, -71, -71, -70, -69, -68, -68, -67, -66, -66, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -56, -56, -55, -54, -53, -53, -52, -51, -51, -50, -49, -48, -48, -47, -46, -46, -45, -44, -43, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, -33, -32, -31, -31, -30, -29, -28, -28, -27, -26, -26, -25, -24, -23, -23, -22, -21, -21, -20, -19, -18, -18, -17, -16, -16, -15, -14, -13, -13, -12, -11, -11, -10, -9, -8, -8, -7, -6, -6, -5, -4, -3, -3, -2, -1, 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, 24, 25, 25, 26, 27, 27, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 40, 40, 41, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 49, 50, 50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 57, 58, 59, 60, 60, 61, 62, 62, 63, 64, 65, 65, 66, 67, 67, 68, 69, 70, 70, 71, 72, 72, 73, 74, 75, 75, 76, 77, 77, 78, 79, 80, 80, 81, 82, 82, 83, 84, 85, 85, 86, 87, 87, 88, 89, 90, 90 };
const int Table_fu1[256]={ -44, -44, -44, -43, -43, -43, -42, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -39, -38, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -25, -24, -24, -24, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43 };
const int Table_fu2[256]={ -227, -226, -224, -222, -220, -219, -217, -215, -213, -212, -210, -208, -206, -204, -203, -201, -199, -197, -196, -194, -192, -190, -188, -187, -185, -183, -181, -180, -178, -176, -174, -173, -171, -169, -167, -165, -164, -162, -160, -158, -157, -155, -153, -151, -149, -148, -146, -144, -142, -141, -139, -137, -135, -134, -132, -130, -128, -126, -125, -123, -121, -119, -118, -116, -114, -112, -110, -109, -107, -105, -103, -102, -100, -98, -96, -94, -93, -91, -89, -87, -86, -84, -82, -80, -79, -77, -75, -73, -71, -70, -68, -66, -64, -63, -61, -59, -57, -55, -54, -52, -50, -48, -47, -45, -43, -41, -40, -38, -36, -34, -32, -31, -29, -27, -25, -24, -22, -20, -18, -16, -15, -13, -11, -9, -8, -6, -4, -2, 0, 1, 3, 5, 7, 8, 10, 12, 14, 15, 17, 19, 21, 23, 24, 26, 28, 30, 31, 33, 35, 37, 39, 40, 42, 44, 46, 47, 49, 51, 53, 54, 56, 58, 60, 62, 63, 65, 67, 69, 70, 72, 74, 76, 78, 79, 81, 83, 85, 86, 88, 90, 92, 93, 95, 97, 99, 101, 102, 104, 106, 108, 109, 111, 113, 115, 117, 118, 120, 122, 124, 125, 127, 129, 131, 133, 134, 136, 138, 140, 141, 143, 145, 147, 148, 150, 152, 154, 156, 157, 159, 161, 163, 164, 166, 168, 170, 172, 173, 175, 177, 179, 180, 182, 184, 186, 187, 189, 191, 193, 195, 196, 198, 200, 202, 203, 205, 207, 209, 211, 212, 214, 216, 218, 219, 221, 223, 225 };


bool YV12_To_RGB24(const unsigned char* pYV12, unsigned char* &pRGB24, const int& width, const int& height)
{
    if(NULL == pYV12)
        return false;

    long buffsize = width * 24 * height;

    pRGB24 = new unsigned char[buffsize];

    if(!pYV12 || !pRGB24)
        return false;

    const long nYLen = long(width*height);
    const int nHfWidth = (width>>1);

    if(nYLen<1 || nHfWidth<1)
        return false;

    // Y data
    const unsigned char* yData = pYV12;
    // v data
    const unsigned char* vData = &yData[nYLen];
    // u data
    const unsigned char* uData = &vData[nYLen>>2];

    if(!uData || !vData)
        return false;

    int rgb[3];
    int i, j, m, n, x, y, py, rdif, invgdif, bdif;
    m = -width;
    n = -nHfWidth;

    bool addhalf = true;
    for(y=0; y<height;y++) {
        m += width;
        if( addhalf ){
            n+=nHfWidth;
            addhalf = false;
        } else {
            addhalf = true;
        }
        for(x=0; x<width;x++)  {
            i = m + x;
            j = n + (x>>1);

            py = yData[i];

            // search tables to get rdif invgdif and bidif
            rdif = Table_fv1[vData[j]];    // fv1
            invgdif = Table_fu1[uData[j]] + Table_fv2[vData[j]]; // fu1+fv2
            bdif = Table_fu2[uData[j]]; // fu2

            rgb[2] = py+rdif;    // R
            rgb[1] = py-invgdif; // G
            rgb[0] = py+bdif;    // B

            //j = nYLen - width - m + x;//Õâžö»áÍŒÏñµßµ¹
            j = m + x;
            i = (j<<1) + j;

            // copy this pixel to rgb data
            for(j=0; j<3; j++){
                if(rgb[j]>=0 && rgb[j]<=255){
                    pRGB24[i + j] = rgb[j];
                }else{
                    pRGB24[i + j] = (rgb[j] < 0)? 0 : 255;
                }
            }
        }
    }
    return true;
}
